package cart

import (
	"sort"
	"strconv"
	"strings"

	"mall/common/globalkey"
	"mall/common/moneyx"
	"mall/common/utils/pointy"
	"mall/service/forum/api/internal/biz/promotion"
	"mall/service/forum/api/internal/types"
	"mall/service/product/rpc/productclient"
	"mall/service/promotion/rpc/promotionclient"

	"github.com/shopspring/decimal"
	"github.com/zeromicro/go-zero/core/logx"
)

// SkuInfoHandler 商品信息handler
type SkuInfoHandler struct {
	// 合成复用Next
	Next
}

// Do 商品信息逻辑
func (h *SkuInfoHandler) Do(c *Context) (err error) {
	// logx.Infow("获取商品信息...")

	limit := uint32(len(c.Resp.List))
	if limit == 0 {
		return
	}

	variationIdMap := make(map[uint64]*types.ShoppingCartItemInfo)
	variationIdKeyMap := make(map[uint64]int)
	var variationIds []string
	for idx, v := range c.Resp.List {
		if v.SkuId != nil && v.Quantity != nil && *v.SkuId > 0 && *v.Quantity > 0 {
			variationIdMap[*v.SkuId] = v
			variationIdKeyMap[*v.SkuId] = idx
			variationIds = append(variationIds, strconv.FormatUint(*v.SkuId, 10))
		}
	}

	if len(variationIds) == 0 {
		return
	}

	preload := "product:Id,ParentId,Title,Slug"
	eagerLoad := "product:productMeta,productRelationship,productTerm,productVariation,productVariationMeta"
	preFilter := "Id,in:" + strings.Join(variationIds, ",")
	preFilterEntity := "productVariation"
	// preFilter += ";Status,equalTo:" + strconv.FormatInt(int64(globalkey.StatusEnabled), 10) // 错误的状态也返回
	preload += ";productRelationship:PerfumeId,PerfumeName,PerfumeSlug,BrandId,BrandName,BrandSlug"
	preload += ";productTerm:Id,Name,Slug,ParentId"
	preload += ";productMeta:mediaId,mediaUrl"
	preload += ";productVariation:Id,GemPrice,RetailPrice,CounterPrice,Weight,Status"
	preload += ";productVariationMeta:stock,mediaId,mediaUrl,sku,skuKey"
	res, err := c.SvcCtx.ProductRpc.GetProductList(c.Ctx, &productclient.BaseListReq{
		Limit:           limit,
		Preload:         pointy.GetPointer(preload),
		EagerLoad:       pointy.GetPointer(eagerLoad),
		PreFilterEntity: pointy.GetPointer(preFilterEntity),
		PreFilter:       pointy.GetPointer(preFilter),
	})
	if err != nil {
		return err
	}

	// logx.Infow("商品库存校验...")

	if len(res.List) > 0 {
		var total int64             // 总件数
		isValid := true             // 库存有效
		var fAmount decimal.Decimal // 人民币
		var fWeight decimal.Decimal // 总重
		for _, item := range res.List {
			if len(item.Skus) > 0 {
				var brandId *uint64
				var brandTitle *string
				var brandSlug *string
				var entryId *uint64
				if len(item.Relations) > 0 {
					for _, v := range item.Relations {
						if v.Key != nil {
							switch *v.Key {
							case globalkey.ProductRelationNameBrand:
								brandId = v.Id
								brandTitle = v.Name
								brandSlug = v.Slug
							case globalkey.ProductRelationNamePerfume:
								entryId = v.Id
							}
						}
					}
				}

				var itemMediaId *string
				var itemMediaUrl *string
				if len(item.Meta) > 0 {
					for _, m := range item.Meta {
						if m.Key == nil || m.Value == nil {
							continue
						}

						val := *m.Value
						switch *m.Key {
						case "mediaId":
							itemMediaId = pointy.GetPointer(val)
						case "mediaUrl":
							itemMediaUrl = pointy.GetPointer(val)
						}
					}
				}

				if len(item.Media) > 0 {
					for _, v := range item.Media {
						var url string
						if v.Url != nil && len(*v.Url) > 0 {
							url = *v.Url
						}
						if len(url) == 0 && v.Uri != nil && len(*v.Uri) > 0 {
							url = *v.Uri
						}
						if len(url) > 0 {
							itemMediaId = v.Id
							itemMediaUrl = pointy.GetPointer(url)
							break
						}
					}
				}

				var categories []*types.CategoryInfo
				var categoryIds []uint64
				if len(item.Categories) > 0 {
					for _, v := range item.Categories {
						categories = append(categories, &types.CategoryInfo{
							Id:       v.Id,
							Name:     v.Name,
							Slug:     v.Slug,
							ParentId: v.ParentId,
						})
						if v.Id != nil && *v.Id > 0 {
							categoryIds = append(categoryIds, *v.Id)
						}
					}
				}

				for _, sku := range item.Skus {
					if sku.Id == nil {
						continue
					}

					var checked bool
					if _, ok := c.CheckedMap[*sku.Id]; ok {
						checked = true
					}
					var number uint32
					var prefetched *uint32
					if info, ok := variationIdMap[*sku.Id]; ok {
						number = *info.Quantity
						prefetched = info.Prefetched
					}
					if idx, ok := variationIdKeyMap[*sku.Id]; ok {
						reqNumber := int64(number)
						if checked {
							total += reqNumber // 已选 total 件
						}

						var status int32
						if sku.Inventory != nil && number > *sku.Inventory { // 超出库存
							status = globalkey.StatusOverSize
							isValid = false
						} else if sku.Status != nil {
							status = int32(*sku.Status)
						}

						var mediaId *string
						if sku.MediaId != nil && len(*sku.MediaId) > 0 {
							mediaId = sku.MediaId
						} else {
							mediaId = itemMediaId
						}

						var mediaUrl *string
						if sku.MediaUrl != nil && len(strings.TrimSpace(*sku.MediaUrl)) > 0 {
							mediaUrl = sku.MediaUrl
						} else {
							mediaUrl = itemMediaUrl
						}

						c.Resp.List[idx] = &types.ShoppingCartItemInfo{
							Id:          c.Resp.List[idx].Id,
							ShopId:      pointy.GetPointer(uint64(1)),
							SkuId:       sku.Id,
							ProductId:   item.Id,
							EntryId:     entryId,
							RetailPrice: sku.RetailPrice,
							Quantity:    pointy.GetPointer(number),
							Inventory:   sku.Inventory,
							Weight:      sku.Weight,
							Title:       item.Title,
							Slug:        item.Slug,
							BrandId:     brandId,
							BrandTitle:  brandTitle,
							BrandSlug:   brandSlug,
							MediaId:     mediaId,
							MediaUrl:    mediaUrl,
							Sku:         sku.Label,
							Status:      pointy.GetPointer(status),
							Categories:  categories,
							Prefetched:  prefetched,
							Selected:    pointy.GetPointer(checked),
							PostId:      c.Resp.List[idx].PostId,
							InviterId:   c.Resp.List[idx].InviterId,
						}

						pricingInfo := &promotion.PricingInfo{
							SkuId:     *sku.Id,
							ProductId: *item.Id,
							Status:    status,
						}

						if sku.RetailPrice != nil && len(*sku.RetailPrice) > 0 {
							if _, ok := c.SkuAmountMap[*sku.Id]; !ok {
								fp, err := moneyx.ParseDecimal(*sku.RetailPrice)
								if err != nil {
									logx.Errorw("failed to ParseDecimal", logx.Field("detail", err.Error()),
										logx.Field("retailPrice", *sku.RetailPrice))
								} else {
									pricingInfo.SkuPrice = fp
									pricingInfo.SkuNumber = reqNumber

									fa := fp.Mul(decimal.NewFromInt(reqNumber))
									c.SkuAmountMap[*sku.Id] = fa

									pricingInfo.SkuAmount = fa
									pricingInfo.FinalAmount = fa

									if checked {
										pricingInfo.Checked = 1
										fAmount = fAmount.Add(fa)
									}
								}
							}
						}

						if checked {
							if sku.Weight != nil && len(*sku.Weight) > 0 {
								if weight, err := moneyx.ParseDecimal(*sku.Weight); err != nil {
									logx.Errorw("failed to ParseDecimal", logx.Field("detail", err.Error()),
										logx.Field("weight", *sku.Weight))
								} else {
									fWeight = fWeight.Add(weight.Mul(decimal.NewFromInt(reqNumber)))
								}
							}
						}

						c.SkuScopeInfos = append(c.SkuScopeInfos, &promotionclient.SkuScopeInfo{
							VariationId: sku.Id,
							ProductId:   item.Id,
							EntryId:     entryId,
							BrandId:     brandId,
							CategoryIds: categoryIds,
						})

						c.PricingInfos = append(c.PricingInfos, pricingInfo)
					}
				}
			}
		}

		c.Resp.StockValid = isValid
		c.Resp.Total = pointy.GetPointer(uint32(total))
		c.Resp.Weight = pointy.GetPointer(moneyx.FormatDecimal(fWeight, 3))
		// c.Resp.GemAmount =
		// 	pointy.GetPointer(moneyx.FormatDecimal(fAmount.Mul(decimal.NewFromInt(int64(globalkey.GemRatio))), 2))
		c.Resp.RetailAmount = pointy.GetPointer(moneyx.FormatDecimal(fAmount, 2))
		c.SkusAmount = fAmount
		c.SkusNumber = total
		c.SkusWeight = fWeight
		c.FinalAmount = fAmount

		sort.Slice(c.PricingInfos, func(i, j int) bool {
			return c.PricingInfos[i].SkuPrice.GreaterThan(c.PricingInfos[j].SkuPrice)
		})
	}

	return
}
